/*
 * VisualBoyAdvanced - Nintendo Gameboy/GameboyAdvance (TM) emulator
 * Copyrigh(c) 1999-2002 Forgotten (vb@emuhq.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <windows.h>

#define UINT8 unsigned char
#define UINT16 unsigned short
#define UINT32 unsigned int
#define INT8 signed char
#define INT16 signed short
#define INT32 signed int
#define SINT32 signed int
#define SINT64 signed __int64

#define PEEK (i286_memoryread( (CPU_IP) + CS_BASE))
#define FETCH (i286_memoryread( (CPU_IP++) + CS_BASE))


typedef enum { ES, CS, SS, DS } SREGS;

//#define RegW(ModRM) Mod_RM.reg.w[ModRM]
//#define RegB(ModRM) Mod_RM.reg.b[ModRM]
//#define RMW(ModRM) Mod_RM.RM.w[ModRM]
//#define RMB(ModRM) Mod_RM.RM.b[ModRM]

int instr = 1;
int seg_prefix;
int seg_base;
int prefix_base;

#pragma warning(disable:4761)

typedef struct {
  UINT8 mask;
  UINT8 value;
  char *mnen;
	UINT8 postfix;
} OPCODE_8086;

static char *registers[] =
	{ "al", "cl", "dl", "bl",
	  "ah", "ch", "dh", "bh",
		"spl", "bpl", "sil", "dil",
		"sph", "bph", "sil", "dih" };

static char *registers16[] =
	{ "ax", "cx", "dx", "bx",
		"sp", "bp", "si", "di" };

static char *cond[] =
  { "nz", "z", "nc", "c" };

static char *base[] =
  { "es", "cs", "ss", "ds" };

static char hexDigits[16] = {
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};

OPCODE_8086 opcodes[] = {
  { 0xff, 0x00, "add %z" },				// add r/m8,r8
  { 0xff, 0x01, "add %Z" },				// add r/m16,r16
  { 0xff, 0x02, "add %y" },				// add r8,r8
  { 0xff, 0x03, "add %Y" },				// add r16,r16
  { 0xff, 0x04, "add al,%B" },		// add al,imm8
  { 0xff, 0x05, "add ax,%W" },		// add ax,imm16
  { 0xff, 0x06, "push es" },
  { 0xff, 0x07, "pop es" },

  { 0xff, 0x08, "or %z" },				// or r/m8,r8
  { 0xff, 0x09, "or %Z" },				// or r/m16,r16
  { 0xff, 0x0a, "or %y" },				// or r8,r8
  { 0xff, 0x0b, "or %Y" },				// or r16,r16
  { 0xff, 0x0c, "or al,%B" },			// or al,imm8
  { 0xff, 0x0d, "or ax,%W" },			// or ax,imm16
  { 0xff, 0x0e, "push cs" },
  { 0xff, 0x0f, "xcts (upper code)" },

  { 0xff, 0x10, "adc %z" },				// adc r/m8,r8
  { 0xff, 0x11, "adc %Z" },				// adc r/m16,r16
  { 0xff, 0x12, "adc %y" },				// adc r8,r8
  { 0xff, 0x13, "adc %Y" },				// adc r16,r16
  { 0xff, 0x14, "adc al,%B" },		// adc al,imm8
  { 0xff, 0x15, "adc ax,%W" },		// adc ax,imm16
  { 0xff, 0x16, "push ss" },
  { 0xff, 0x17, "pop ss" },

  { 0xff, 0x18, "sbb %z" },				// sbb r/m8,r8
  { 0xff, 0x19, "sbb %Z" },				// sbb r/m16,r16
  { 0xff, 0x1a, "sbb %y" },				// sbb r8,r8
  { 0xff, 0x1b, "sbb %Y" },				// sbb r16,r16
  { 0xff, 0x1c, "sbb al,%B" },		// sbb al,imm8
  { 0xff, 0x1d, "sbb ax,%W" },		// sbb ax,imm16
  { 0xff, 0x1e, "push ds" },
  { 0xff, 0x1f, "pop ds" },

  { 0xff, 0x20, "and %z" },				// and r/m8,r8
  { 0xff, 0x21, "and %Z" },				// and r/m16,r16
  { 0xff, 0x22, "and %y" },				// and r8,r8
  { 0xff, 0x23, "and %Y" },				// and r16,r16
  { 0xff, 0x24, "and al,%B" },		// and al,imm8
  { 0xff, 0x25, "and ax,%W" },		// and ax,imm16
//  { 0xff, 0x26, "repe es" },
  { 0xff, 0x27, "daa" },

  { 0xff, 0x28, "sub %z" },				// sub r/m8,r8
  { 0xff, 0x29, "sub %Z" },				// sub r/m16,r16
  { 0xff, 0x2a, "sub %y" },				// sub r8,r8
  { 0xff, 0x2b, "sub %Y" },				// sub r16,r16
  { 0xff, 0x2c, "sub al,%B" },		// sub al,imm8
  { 0xff, 0x2d, "sub ax,%W" },		// sub ax,imm16
//  { 0xff, 0x2e, "repe cs" },
  { 0xff, 0x2f, "das" },

  { 0xff, 0x30, "xor %z" },				// xor r/m8,r8
  { 0xff, 0x31, "xor %Z" },				// xor r/m16,r16
  { 0xff, 0x32, "xor %y" },				// xor r8,r8
  { 0xff, 0x33, "xor %Y" },				// xor r16,r16
  { 0xff, 0x34, "xor al,%B" },		// xor al,imm8
  { 0xff, 0x35, "xor ax,%W" },		// xor ax,imm16
//  { 0xff, 0x36, "repe ss" },
  { 0xff, 0x37, "aaa" },

  { 0xff, 0x38, "cmp %z" },				// cmp r/m8,r8
  { 0xff, 0x39, "cmp %Z" },				// cmp r/m16,r16
  { 0xff, 0x3a, "cmp %y" },				// cmp r8,r8
  { 0xff, 0x3b, "cmp %Y" },				// cmp r16,r16
  { 0xff, 0x3c, "cmp al,%B" },		// cmp al,imm8
  { 0xff, 0x3d, "cmp ax,%W" },		// cmp ax,imm16
//  { 0xff, 0x3e, "repe ds" },
  { 0xff, 0x3f, "aas" },

  { 0xff, 0x40, "inc ax" },
  { 0xff, 0x41, "inc cx" },
  { 0xff, 0x42, "inc dx" },
  { 0xff, 0x43, "inc bx" },
  { 0xff, 0x44, "inc sp" },
  { 0xff, 0x45, "inc bp" },
  { 0xff, 0x46, "inc si" },
  { 0xff, 0x47, "inc di" },

  { 0xff, 0x48, "dec ax" },
  { 0xff, 0x49, "dec cx" },
  { 0xff, 0x4a, "dec dx" },
  { 0xff, 0x4b, "dec bx" },
  { 0xff, 0x4c, "dec sp" },
  { 0xff, 0x4d, "dec bp" },
  { 0xff, 0x4e, "dec si" },
  { 0xff, 0x4f, "dec di" },

  { 0xff, 0x50, "push ax" },
  { 0xff, 0x51, "push cx" },
  { 0xff, 0x52, "push dx" },
  { 0xff, 0x53, "push bx" },
  { 0xff, 0x54, "push sp" },
  { 0xff, 0x55, "push bp" },
  { 0xff, 0x56, "push si" },
  { 0xff, 0x57, "push di" },

  { 0xff, 0x58, "pop ax" },
  { 0xff, 0x59, "pop cx" },
  { 0xff, 0x5a, "pop dx" },
  { 0xff, 0x5b, "pop bx" },
  { 0xff, 0x5c, "pop sp" },
  { 0xff, 0x5d, "pop bp" },
  { 0xff, 0x5e, "pop si" },
  { 0xff, 0x5f, "pop di" },

  { 0xff, 0x60, "pusha" },
  { 0xff, 0x61, "popa" },
  { 0xff, 0x62, "bound %R,%W" },	// bound r16,m16&m16
  { 0x00, 0x63, "arpl" },
  { 0xff, 0x64, "nop (INT 6)" },
  { 0xff, 0x65, "nop (INT 6)" },
  { 0xff, 0x66, "nop (INT 6)" },
  { 0xff, 0x67, "nop (INT 6)" },

  { 0xff, 0x68, "push %W" },
  { 0xff, 0x69, "imul %X,%W" },		// imul r16,r/m16,imm16
  { 0xff, 0x6a, "push %B" },
  { 0xff, 0x6b, "imul %x,%B" },		// imul r8,r/m8,imm8
  { 0xff, 0x6c, "rep insb" },
  { 0xff, 0x6d, "rep insw" },
  { 0xff, 0x6e, "rep outsb" },
  { 0xff, 0x6f, "rep outsw" },

	{ 0xff, 0x70, "jo %d" },
	{ 0xff, 0x71, "jno %d" },
	{ 0xff, 0x72, "jc %d" },
	{ 0xff, 0x73, "jnc %d" },
	{ 0xff, 0x74, "jz %d" },
	{ 0xff, 0x75, "jnz %d" },
	{ 0xff, 0x76, "jbe %d" },
	{ 0xff, 0x77, "jnbe %d" },

	{ 0xff, 0x78, "js %d" },
	{ 0xff, 0x79, "jns %d" },
	{ 0xff, 0x7a, "jp %d" },
	{ 0xff, 0x7b, "jnp %d" },
	{ 0xff, 0x7c, "jl %d" },
	{ 0xff, 0x7d, "jnl %d" },
	{ 0xff, 0x7e, "jle %d" },
	{ 0xff, 0x7f, "jnle %d" },

//  { 0xff, 0x80, "" },
//  { 0xff, 0x81, "" },
//  { 0xff, 0x82, "" },
//  { 0xff, 0x83, "" },
	{ 0xff, 0x84, "test %z" },					// test r/m8,r8
	{ 0xff, 0x85, "test %Z" },					// test r/m16,r16
	{ 0xff, 0x86, "xchg %z" },					// test r8,r/m8
	{ 0xff, 0x87, "xchg %Z" },					// test r16,r/m16

	{ 0xff, 0x88, "mov %z" },						// mov r/m8,r8
	{ 0xff, 0x89, "mov %Z" },						// mov r/m16,r16
	{ 0xff, 0x8a, "mov %y" },						// mov r8,r/m8
	{ 0xff, 0x8b, "mov %Y" },						// mov r16,r/m16
	{ 0xff, 0x8c, "mov %R,%S" },				// mov r/m16,Sreg
	{ 0xff, 0x8d, "lea %R,%W" },				// lea r16,m16
  { 0xff, 0x8e, "mov segrem, EA" },
	{ 0xff, 0x8f, "pop %R" },						// pop r16

	{ 0xff, 0x90, "nop (BIOS)" },
	{ 0xff, 0x91, "xchg ax,cx" },
	{ 0xff, 0x92, "xchg ax,dx" },
	{ 0xff, 0x93, "xchg ax,bx" },
	{ 0xff, 0x94, "xchg ax,sp" },
	{ 0xff, 0x95, "xchg ax,bp" },
	{ 0xff, 0x96, "xchg ax,si" },
	{ 0xff, 0x97, "xchg ax,di" },

	{ 0xff, 0x98, "cbw ax" },
	{ 0xff, 0x99, "cwd dx" },
	{ 0xff, 0x9a, "call %A" },					// call ptr16:16
	{ 0xff, 0x9b, "wait" },
	{ 0xff, 0x9c, "pushf" },
	{ 0xff, 0x9d, "popf" },
	{ 0xff, 0x9e, "sahf" },
	{ 0xff, 0x9f, "lahf" },

	{ 0xff, 0xa0, "mov al,%S:[%W]" },	// mov al,moffs8
	{ 0xff, 0xa1, "mov ax,%S:[%W]" },	// mov ax,moffs16
	{ 0xff, 0xa2, "mov %S:[%W],al" },	// mov moffs8,al
	{ 0xff, 0xa3, "mov %S:[%W],ax" },	// mov moffs16,ax
	{ 0xff, 0xa4, "movsb" },					// mov es:[di],ds:[si]
	{ 0xff, 0xa5, "movsw" },					// mov es:[di],ds:[si]
	{ 0xff, 0xa6, "cmpsb" },
	{ 0xff, 0xa7, "cmpsw" },

	{ 0xff, 0xa8, "test al,%B" },
	{ 0xff, 0xa9, "test ax,%B" },
	{ 0xff, 0xaa, "stosb" },
	{ 0xff, 0xab, "stosw" },
	{ 0xff, 0xac, "lodsb" },
	{ 0xff, 0xad, "lodsw" },
	{ 0xff, 0xae, "scasb" },
	{ 0xff, 0xaf, "scasw" },

	{ 0xff, 0xb0, "mov al,%B" },
	{ 0xff, 0xb1, "mov cl,%B" },
	{ 0xff, 0xb2, "mov dl,%B" },
	{ 0xff, 0xb3, "mov bl,%B" },
	{ 0xff, 0xb4, "mov ah,%B" },
	{ 0xff, 0xb5, "mov ch,%B" },
	{ 0xff, 0xb6, "mov dh,%B" },
	{ 0xff, 0xb7, "mov bh,%B" },

	{ 0xff, 0xb8, "mov ax,%W" },
	{ 0xff, 0xb9, "mov cx,%W" },
	{ 0xff, 0xba, "mov dx,%W" },
	{ 0xff, 0xbb, "mov bx,%W" },
	{ 0xff, 0xbc, "mov sp,%W" },
	{ 0xff, 0xbd, "mov bp,%W" },
	{ 0xff, 0xbe, "mov si,%W" },
	{ 0xff, 0xbf, "mov di,%W" },

	{ 0xff, 0xc0, "shift %r,%B" },			// shift r/m8,imm8
	{ 0xff, 0xc1, "shift %R,%B" },			// shift r/m16,imm8
	{ 0xff, 0xc2, "ret %W" },
	{ 0xff, 0xc3, "ret" },
	{ 0xff, 0xc4, "les %R,%A" },				// les r16,m16:16
	{ 0xff, 0xc5, "lds %R,%A" },				// lds r16,m16:16
	{ 0xff, 0xc6, "mov %r,%B" },				// mov r/m8,imm8
	{ 0xff, 0xc7, "mov %R,%W" },				// mov r/m16,imm16

	{ 0xff, 0xc8, "enter %W,%B" },
	{ 0xff, 0xc9, "leave" },
	{ 0xff, 0xca, "retf %W" },
	{ 0xff, 0xcb, "retf" },
	{ 0xff, 0xcc, "int 3" },
	{ 0xff, 0xcd, "int %B" },
	{ 0xff, 0xce, "into" },
	{ 0xff, 0xcf, "iret" },

	{ 0xff, 0xd0, "shift %r,1" },				// shift r/m8,1
	{ 0xff, 0xd1, "shift %R,1" },				// shift r/m16,1
	{ 0xff, 0xd2, "shift %r,cl" },			// shift r/m8,cl
	{ 0xff, 0xd3, "shift %R,cl" },			// shift r/m16,cl
	{ 0xff, 0xd4, "aam %B" },
	{ 0xff, 0xd5, "aad %B" },
	{ 0xff, 0xd6, "setalc" },
	{ 0xff, 0xd7, "xlatb" },

	{ 0xff, 0xd8, "esc" },
	{ 0xff, 0xd9, "esc" },
	{ 0xff, 0xda, "esc" },
	{ 0xff, 0xdb, "esc" },
	{ 0xff, 0xdc, "esc" },
	{ 0xff, 0xdd, "esc" },
	{ 0xff, 0xde, "esc" },
	{ 0xff, 0xdf, "esc" },

  { 0xff, 0xe0, "loopne %d" },
  { 0xff, 0xe1, "loope %d" },
  { 0xff, 0xe2, "loop %d" },
  { 0xff, 0xe3, "jcxz %d" },
  { 0xff, 0xe4, "in al,%B" },
  { 0xff, 0xe5, "in ax,%B" },
  { 0xff, 0xe6, "out %B,al" },
  { 0xff, 0xe7, "out %B,ax" },

  { 0xff, 0xe8, "call %D" },
  { 0xff, 0xe9, "jmp %D" },
  { 0xff, 0xea, "jmp %A" },							// jmp ptr16:16
  { 0xff, 0xeb, "jmp %d" },
  { 0xff, 0xec, "in al,dx" },
  { 0xff, 0xed, "in ax,dx" },
  { 0xff, 0xee, "out dx,al" },
  { 0xff, 0xef, "out dx,ax" },

  { 0xff, 0xf0, "lock" },
  { 0xff, 0xf1, "lock" },
  { 0xff, 0xf2, "repne" },
  { 0xff, 0xf3, "repe" },
  { 0xff, 0xf4, "hlt" },
  { 0xff, 0xf5, "cmc" },
  { 0xff, 0xf6, "op f6" },
  { 0xff, 0xf7, "op f7" },

  { 0xff, 0xf8, "clc" },
  { 0xff, 0xf9, "stc" },
  { 0xff, 0xfa, "cli" },
  { 0xff, 0xfb, "sti" },
  { 0xff, 0xfc, "cld" },
  { 0xff, 0xfd, "std" },
  { 0xff, 0xfe, "op fe" },
  { 0xff, 0xff, "op ff" },

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};


OPCODE_8086 Opcodes_80[] = {
  { 0x38, 0x00, "add %r,%B" },				// add r/m8,imm8
  { 0x38, 0x08, "or %r,%B" },					// or r/m8,imm8
  { 0x38, 0x10, "adc %r,%B" },				// adc r/m8,imm8
  { 0x38, 0x18, "sbb %r,%B" },				// sbb r/m8,imm8
  { 0x38, 0x20, "and %r,%B" },				// and r/m8,imm8
  { 0x38, 0x28, "sub %r,%B" },				// sub r/m8,imm8
  { 0x38, 0x30, "xor %r,%B" },				// xor r/m8,imm8
  { 0x38, 0x38, "cmp %r,%B" },				// cmp r/m8,imm8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_81[] = {
  { 0x38, 0x00, "add %R,%W" },				// add r/m16,imm16
  { 0x38, 0x08, "or %R,%W" },					// or r/m16,imm16
  { 0x38, 0x10, "adc %R,%W" },				// adc r/m16,imm16
  { 0x38, 0x18, "sbb %R,%W" },				// sbb r/m16,imm16
  { 0x38, 0x20, "and %R,%W" },				// and r/m16,imm16
  { 0x38, 0x28, "sub %R,%W" },				// sub r/m16,imm16
  { 0x38, 0x30, "xor %R,%W" },				// xor r/m16,imm16
  { 0x38, 0x38, "cmp %R,%W" },				// cmp r/m16,imm16

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_82[] = {
  { 0x38, 0x00, "add %r,%B" },				// add r/m8,imm8
  { 0x38, 0x08, "or %r,%B" },					// or r/m8,imm8
  { 0x38, 0x10, "adc %r,%B" },				// adc r/m8,imm8
  { 0x38, 0x18, "sbb %r,%B" },				// sbb r/m8,imm8
  { 0x38, 0x20, "and %r,%B" },				// and r/m8,imm8
  { 0x38, 0x28, "sub %r,%B" },				// sub r/m8,imm8
  { 0x38, 0x30, "xor %r,%B" },				// xor r/m8,imm8
  { 0x38, 0x38, "cmp %r,%B" },				// cmp r/m8,imm8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_83[] = {
  { 0x38, 0x00, "add %R,%B" },				// add r/m16,imm8
  { 0x38, 0x08, "or %R,%B" },					// or r/m16,imm8
  { 0x38, 0x10, "adc %R,%B" },				// adc r/m16,imm8
  { 0x38, 0x18, "sbb %R,%B" },				// sbb r/m16,imm8
  { 0x38, 0x20, "and %R,%B" },				// and r/m16,imm8
  { 0x38, 0x28, "sub %R,%B" },				// sub r/m16,imm8
  { 0x38, 0x30, "xor %R,%B" },				// xor r/m16,imm8
  { 0x38, 0x38, "cmp %R,%B" },				// cmp r/m16,imm8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_8e[] = {
  { 0x38, 0x00, "mov es,%R" },				// mov es,r/m16
  { 0x38, 0x08, "mov cs,%R" },				// mov cs,r/m16
  { 0x38, 0x10, "mov ss,%R" },				// mov ss,r/m16
  { 0x38, 0x18, "mov ds,%R" },				// mov ds,r/m16

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_c0[] = {
  { 0x38, 0x00, "rol %r,%B" },				// rol r/m8,imm8
  { 0x38, 0x08, "ror %r,%B" },				// ror r/m8,imm8
  { 0x38, 0x10, "rcl %r,%B" },				// rcl r/m8,imm8
  { 0x38, 0x18, "rcr %r,%B" },				// rcr r/m8,imm8
  { 0x38, 0x20, "shl %r,%B" },				// shl r/m8,imm8
  { 0x38, 0x28, "shr %r,%B" },				// shr r/m8,imm8
  { 0x38, 0x30, "nop (2)" },					// nop, nop
  { 0x38, 0x38, "sar %r,%B" },				// sar r/m8,imm8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_c1[] = {
  { 0x38, 0x00, "rol %R,%B" },				// rol r/m16,imm8
  { 0x38, 0x08, "ror %R,%B" },				// ror r/m16,imm8
  { 0x38, 0x10, "rcl %R,%B" },				// rcl r/m16,imm8
  { 0x38, 0x18, "rcr %R,%B" },				// rcr r/m16,imm8
  { 0x38, 0x20, "shl %R,%B" },				// shl r/m16,imm8
  { 0x38, 0x28, "shr %R,%B" },				// shr r/m16,imm8
  { 0x38, 0x30, "nop (2)" },					// nop, nop
  { 0x38, 0x38, "sar %R,%B" },				// sar r/m16,imm8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_d0[] = {
  { 0x38, 0x00, "rol %r,1" },				// rol r/m8,1
  { 0x38, 0x08, "ror %r,1" },				// ror r/m8,1
  { 0x38, 0x10, "rcl %r,1" },				// rcl r/m8,1
  { 0x38, 0x18, "rcr %r,1" },				// rcr r/m8,1
  { 0x38, 0x20, "shl %r,1" },				// shl r/m8,1
  { 0x38, 0x28, "shr %r,1" },				// shr r/m8,1
  { 0x38, 0x30, "nop (2)" },				// nop, nop
  { 0x38, 0x38, "sar %r,1" },				// sar r/m8,1

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_d1[] = {
  { 0x38, 0x00, "rol %R,1" },				// rol r/m16,1
  { 0x38, 0x08, "ror %R,1" },				// ror r/m16,1
  { 0x38, 0x10, "rcl %R,1" },				// rcl r/m16,1
  { 0x38, 0x18, "rcr %R,1" },				// rcr r/m16,1
  { 0x38, 0x20, "shl %R,1" },				// shl r/m16,1
  { 0x38, 0x28, "shr %R,1" },				// shr r/m16,1
  { 0x38, 0x30, "nop (2)" },				// nop, nop
  { 0x38, 0x38, "sar %R,1" },				// sar r/m16,1

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_d2[] = {
  { 0x38, 0x00, "rol %r,cl" },				// rol r/m8,cl
  { 0x38, 0x08, "ror %r,cl" },				// ror r/m8,cl
  { 0x38, 0x10, "rcl %r,cl" },				// rcl r/m8,cl
  { 0x38, 0x18, "rcr %r,cl" },				// rcr r/m8,cl
  { 0x38, 0x20, "shl %r,cl" },				// shl r/m8,cl
  { 0x38, 0x28, "shr %r,cl" },				// shr r/m8,cl
  { 0x38, 0x30, "nop (2)" },					// nop, nop
  { 0x38, 0x38, "sar %r,cl" },				// sar r/m8,cl

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_d3[] = {
  { 0x38, 0x00, "rol %R,cl" },				// rol r/m16,cl
  { 0x38, 0x08, "ror %R,cl" },				// ror r/m16,cl
  { 0x38, 0x10, "rcl %R,cl" },				// rcl r/m16,cl
  { 0x38, 0x18, "rcr %R,cl" },				// rcr r/m16,cl
  { 0x38, 0x20, "shl %R,cl" },				// shl r/m16,cl
  { 0x38, 0x28, "shr %R,cl" },				// shr r/m16,cl
  { 0x38, 0x30, "nop (2)" },					// nop, nop
  { 0x38, 0x38, "sar %R,cl" },				// sar r/m16,cl

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_f6[] = {
  { 0x38, 0x00, "test %r,%B" },				// test r/m8,imm8
  { 0x38, 0x08, "nop (2)" },					// nop, nop
  { 0x38, 0x10, "not %r" },						// nop r/m8
  { 0x38, 0x18, "neg %r" },						// neg r/m8
  { 0x38, 0x20, "mul %r" },						// mul r/m8
  { 0x38, 0x28, "imul %r" },					// imul r/m8
  { 0x38, 0x30, "div %r" },						// div r/m8
  { 0x38, 0x38, "idiv %r" },					// idiv r/m8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_f7[] = {
  { 0x38, 0x00, "test %R,%W" },				// test r/m16,imm16
  { 0x38, 0x08, "nop (2)" },					// nop, nop
  { 0x38, 0x10, "not %R" },						// nop r/m16
  { 0x38, 0x18, "neg %R" },						// neg r/m16
  { 0x38, 0x20, "mul %R" },						// mul r/m16
  { 0x38, 0x28, "imul %R" },					// imul r/m16
  { 0x38, 0x30, "div %R" },						// div r/m16
  { 0x38, 0x38, "idiv %R" },					// idiv r/m16

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_fe[] = {
  { 0x38, 0x00, "inc %r" },						// inc r/m8
  { 0x38, 0x08, "dec %r" },						// dec r/m8

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

OPCODE_8086 Opcodes_ff[] = {
  { 0x38, 0x00, "inc %R" },						// inc r/m16
  { 0x38, 0x08, "dec %R" },						// dec r/m16
  { 0x38, 0x10, "call [%W]" },				// call [m16]
  { 0x38, 0x18, "call [%A]" },				// call [m16:16]
  { 0x38, 0x20, "jmp [%W]" },					// jmp [m16]
  { 0x38, 0x28, "jmp [%A]" },					// jmp [m16:16]
  { 0x38, 0x30, "push [%W]" },				// push [m16]

//  { 0xff, 0x00, "" },
  { 0x00, 0x00, "ILLEGAL" },
};

char *GetEA[192]={
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",
	"[bx+si]", "[bx+di]", "[bp+si]", "[bp+di]", "[si]", "[di]", "[", "[bx]",

	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",

	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+",
	"[bx+si+", "[bx+di+", "[bp+si+", "[bp+di+", "[si+", "[di+", "[bp+", "[bx+"
};


static char *addHex(char *p, UINT8 value)
{
  *p++ = hexDigits[value >> 4];
  *p++ = hexDigits[value & 15];
  return p;
}

static char *addHex16(char *p, UINT16 value)
{
  p = addHex(p, value>>8);
  return addHex(p, value & 255);
}

static char *addStr(char *p, char *s)
{
  while(*s) {
    *p++ = *s++;
  }
  return p;
}

static char *addRMB(char *p, UINT32 ModRM) {
	// Mod reg R/M
	//* 00: [reg + scaled index]
  //* 01: [reg + (scaled index) + d8]
  //* 10: [reg + (scaled index) + d32]
  //* 11: register direct

	if( ( ModRM ) >= 0xc0 )
		//p = addStr( p, registers[ Mod_RM.RM.b[ ModRM ] ] );
		p = addStr( p, registers[ ModRM & 7 ] );
	else
	{
		switch( ModRM & 7 ) {
		case 2: case 3: seg_base = SS; break;
		default: seg_base = DS; break;
		}
		if( seg_prefix && ( seg_base == DS || seg_base == SS ) )
			p = addStr( p, base[ prefix_base ] );
		else
			p = addStr( p, base[ seg_base ] );
		p = addStr( p, ":" );

		p = addStr( p, GetEA[ ModRM ] );

		if( ( ModRM & 7 ) == 6 && ModRM < 8*8 ) {
      int b0 = FETCH;
      int b1 = FETCH;
			p = addHex16(p, b0|b1<<8);
      instr += 2;
      *p++ = 'h';
      *p++ = ']';
		}
		else if( ModRM >= 8*8 && ModRM < 8*16 ) {
      p = addHex(p, FETCH);
      *p++ = 'h';
      *p++ = ']';
      instr++;
		}
		else if( ModRM >= 8*16 && ModRM < 8*24 ) {
      int b0 = FETCH;
      int b1 = FETCH;
			p = addHex16(p, b0|b1<<8);
      instr += 2;
      *p++ = 'h';
      *p++ = ']';
		}
	}
	return p;
}

static char *addRMW(char *p, UINT32 ModRM) {
	if( ( ModRM ) >= 0xc0 )
		//p = addStr( p, registers16[ Mod_RM.RM.w[ ModRM ] ] );
		p = addStr( p, registers16[ ModRM & 7 ] );
	else {
		switch( ModRM & 7 ) {
		case 2: case 3: seg_base = SS; break;
		default: seg_base = DS; break;
		}
		if( seg_prefix && ( seg_base == DS || seg_base == SS ) )
			p = addStr( p, base[ prefix_base ] );
		else
			p = addStr( p, base[ seg_base ] );
		p = addStr( p, ":" );

		p = addStr( p, GetEA[ ModRM ] );

		if( ( ModRM & 7 ) == 6 && ModRM < 8*8 ) {
      int b0 = FETCH;
      int b1 = FETCH;
			p = addHex16(p, b0|b1<<8);
      instr += 2;
      *p++ = 'h';
      *p++ = ']';
		}
		else if( ModRM >= 8*8 && ModRM < 8*16 ) {
      p = addHex(p, FETCH);
      *p++ = 'h';
      *p++ = ']';
      instr++;
		}
		else if( ModRM >= 8*16 && ModRM < 8*24 ) {
      int b0 = FETCH;
      int b1 = FETCH;
			p = addHex16(p, b0|b1<<8);
      instr += 2;
      *p++ = 'h';
      *p++ = ']';
		}
	}
	return p;
}


static char first=1;
static OPCODE_8086 *nil;

void Dis_8086(char *buffer, UINT32 address)
{
  char *p = buffer;
  UINT16 addr = address;
	UINT8 opcode;
  char *mnen;
  OPCODE_8086 *op;
  UINT8 b0, b1, b2, b3;
  UINT32 disp;
  int shift;
	int i;
	int add;
	int prefetch;
	
	int pc_save;
	UINT32 ModRM;

	// save PC
	pc_save = CPU_IP;
	instr = 1;

	if(first)
	{
		op = opcodes;
	  while(op->mask!=0) op++;
		nil = op;
		first = 0;
	}

  sprintf(p, "                                          ");
  p += 24;
  
	// grab opcode information
  opcode = FETCH;
	seg_prefix = 0;
	prefix_base = 0;
	seg_base = 0;

restart:
	prefetch = 0;
  if( opcode == 0x26 ) {
    opcode = FETCH;
    instr++;
		
		prefix_base = ES; seg_prefix = 1;

    goto restart;
	}
  else if( opcode == 0x2e ) {
    opcode = FETCH;
    instr++;
		
		prefix_base = CS; seg_prefix = 1;

    goto restart;
	}
  else if( opcode == 0x36 ) {
    opcode = FETCH;
    instr++;
		
		prefix_base = SS; seg_prefix = 1;

    goto restart;
	}
  else if( opcode == 0x3e ) {
    opcode = FETCH;
    instr++;
		
		prefix_base = DS; seg_prefix = 1;

    goto restart;
	}
  else if( opcode == 0x80 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_80;
	}
  else if( opcode == 0x81 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_81;
	}
  else if( opcode == 0x82 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_82;
	}
  else if( opcode == 0x83 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_83;
	}
  else if( opcode == 0x8c ) {
		seg_prefix = 1;
		seg_base = DS;
		prefix_base = (PEEK & 0x38) >> 3;
    op = opcodes;
	}
  else if( opcode == 0x8e ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_8e;
	}
  else if( opcode >= 0xa0 && opcode <= 0xa3 ) {
		seg_base = DS;
		op = opcodes;
	}
  else if( opcode == 0xc0 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_c0;
	}
  else if( opcode == 0xc1 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_c1;
	}
  else if( opcode == 0xd0 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_d0;
	}
  else if( opcode == 0xd1 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_d1;
	}
  else if( opcode == 0xd2 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_d2;
	}
  else if( opcode == 0xd3 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_d3;
	}
  else if( opcode == 0xf2 ) {
    opcode = FETCH;
    instr++;

		p = addStr( p, "repne " ); 

		switch( opcode ) {
		case 0x26: prefix_base = ES; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x2e: prefix_base = CS; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x36: prefix_base = SS; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x3e: prefix_base = DS; seg_prefix = 1; opcode = FETCH; instr++; break;
		}

    goto restart;
	}
  else if( opcode == 0xf3 ) {
    opcode = FETCH;
    instr++;
		
		p = addStr( p, "repe " );

		switch( opcode ) {
		case 0x26: prefix_base = ES; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x2e: prefix_base = CS; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x36: prefix_base = SS; seg_prefix = 1; opcode = FETCH; instr++; break;
		case 0x3e: prefix_base = DS; seg_prefix = 1; opcode = FETCH; instr++; break;
		}

    goto restart;
	}
  else if( opcode == 0xf6 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_f6;
	}
  else if( opcode == 0xf7 ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_f7;
	}
  else if( opcode == 0xfe ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_fe;
	}
  else if( opcode == 0xff ) {
    opcode = FETCH;
		prefetch = 1;
    op = Opcodes_ff;
	}
	else
		op = opcodes;

  while( op->value != ( opcode & op->mask ) ) op++;
	if( op->postfix ) {
		int check = op->value;

		opcode = FETCH;
		instr++;
		while(op->postfix != (opcode & op->mask) && op->value == check) op++;
		if(op->value != check) op=nil;
	}
  mnen = op->mnen;

	//if(opcode==0x03)
		//opcode+=0;

  while( *mnen ) {
    if( *mnen == '%' ) {
      mnen++;
      switch(*mnen++) {

			case 'z':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addRMB( p,ModRM );
				p = addStr( p, "," );
				p = addStr( p, registers[ (ModRM & 0x38) >> 3 ] );
        instr++;
				break;

			case 'Z':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addRMW( p,ModRM );
				p = addStr( p, "," );
				p = addStr( p, registers16[ (ModRM & 0x38) >> 3 ] );
        instr++;
				break;

			case 'y':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addStr( p, registers[ (ModRM & 0x38) >> 3 ] );
				p = addStr( p, "," );
				p = addRMB( p,ModRM );
        instr++;
				break;

			case 'Y':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addStr( p, registers16[ (ModRM & 0x38) >> 3 ] );
				p = addStr( p, "," );
				p = addRMW( p,ModRM );
        instr++;
				break;

			case 'x':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addStr( p, registers[ (ModRM & 0x38) >> 3 ] );
				p = addStr( p, "," );
				p = addRMB( p,ModRM );
        instr++;
				break;

			case 'X':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addStr( p, registers16[ (ModRM & 0x38) >> 3 ] );
				p = addStr( p, "," );
				p = addRMW( p,ModRM );
        instr++;
				break;

 			case 'W':
        b0 = FETCH;
        b1 = FETCH;
				p = addHex16(p, b0|b1<<8);
        instr += 2;
        *p++ = 'h';
        break;

      case 'B':
        p = addHex(p, FETCH);
        *p++ = 'h';
        instr++;
        break;

			case 'D':
        b0 = FETCH;
        b1 = FETCH;
				disp = (unsigned int)(b0|(b1<<8));
        p = addHex16(p, CPU_IP+disp);
        *p++ = 'h';
        instr++;
        instr++;
        break;

			case 'd':
        disp = (unsigned int)((INT8) FETCH);
        p = addHex16(p, CPU_IP+disp);
        *p++ = 'h';
        instr++;
        break;

 			case 'A':
        b0 = FETCH;
        b1 = FETCH;
        b2 = FETCH;
        b3 = FETCH;
				p = addHex16(p, b2|( b3<<8 ));
        *p++ = ':';
				p = addHex16(p, b0|( b1<<8 ));
        *p++ = 'h';
        instr += 4;
        break;

 			case 'R':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addRMW( p, ModRM );
        instr++;
        break;

 			case 'r':
				if( !prefetch ) ModRM = FETCH;
				else ModRM = opcode;
				p = addRMB( p, ModRM );
        instr++;
        break;

			case 'S':
				if( seg_prefix && ( seg_base == DS || seg_base == SS ) )
					p = addStr( p, base[ prefix_base ] );
				else
					p = addStr( p, base[ seg_base ] );
				break;
      }
    } else
      *p++ = *mnen++;
  }
	CPU_IP = pc_save;

 
	for(i = 0; i < instr; i++) {
    UINT16 a = addr + i;
    addHex(buffer+0+i*3, FETCH);
  }
	CPU_IP = pc_save;

  if(p) *p = 0;
}
